﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using MoneyWentWhere.DataModel;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MoneyWentWhere.Test
{
    class TestUtils
    {
        public static void AssertEqual(MoneyDataSet exp, MoneyDataSet act)
        {
            if (exp == act) { throw new ArgumentException("Passing the same DataSet twice"); }


            AssertPublicMembersEqual(exp, act,
                "Currencies", "Accounts", "Categories", "ObjectByGuid", "ExtensionData", "StorageInfo");

            CollectionAssert.AreEquivalent(exp.Currencies, act.Currencies, "Currencies");
            CollectionAssert.AreEquivalent(exp.Categories.ToList(), act.Categories.ToList(), "Categories");            
            AssertEqual(exp.Accounts, act.Accounts);
            CollectionAssert.AreEquivalent(exp.ObjectByGuid.Keys, act.ObjectByGuid.Keys, "ObjectsByGuid");

            // Do not check StorageInfo, filename/format/password may change during file ops
        }

        static void AssertEqual(IList<Account> exp, IList<Account> act)
        {
            Assert.AreEqual(exp.Count, act.Count, "Accounts.Count");
            for (int i = 0; i < exp.Count; i++)
            {
                // Account
                AssertPublicMembersEqual(exp[i], act[i], 
                    "Transactions", "DataSet", "ExtensionData");
            }
        }

        static void AssertEqual(IList<Transaction> exp, IList<Transaction> act)
        {
            Assert.AreEqual(exp.Count, act.Count, "Transactions.Count");
            for (int i = 0; i < exp.Count; i++)
            {
                // Account
                AssertPublicMembersEqual(exp[i], act[i], 
                    "DataSet", "ExtensionData");
            }
        }

        /// <summary>
        /// Assert all public properties and fields are equal
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="exp"></param>
        /// <param name="act"></param>
        /// <param name="excludes"></param>
        public static void AssertPublicMembersEqual<T>(T exp, T act, params string[] excludes)
        {
            var props = typeof(T).GetProperties().Where(x => x.GetMethod != null);
            var fields = typeof(T).GetFields();

            // Validate arg
            if (excludes == null) { excludes = new string[0]; }
            foreach(var exc in excludes)
            {
                if (!props.Any(p => p.Name == exc) &&
                    !fields.Any(f => f.Name == exc))
                {
                    throw new ArgumentException("No property or field: " + exc); 
                }
            }

            // Check properties
            foreach(var p in props)
            {
                if (excludes.Any(x => p.Name == x)) { continue; }

                Assert.AreEqual(p.GetValue(exp), p.GetValue(act), 
                    String.Format("Property {0} on \r\nExp:{1}\r\n{2}", p.Name, exp, act));
            }

            // Check fields
            foreach(var p in fields)
            {
                if (excludes.Any(x => p.Name == x)) { continue; }

                Assert.AreEqual(p.GetValue(exp), p.GetValue(act), 
                    String.Format("Field {0} on \r\nExp:{1}\r\n{2}", p.Name, exp, act));
            }

        }
    }
}
